package com.hospital.vo;

import lombok.Getter;
import lombok.ToString;
import org.springframework.util.ObjectUtils;

import java.io.Serializable;

/**
 * 数据统一响应模型
 *
 * @author coder_zzm
 * @date 2022/5/8 22:06
 */
@Getter
@ToString
public class ResponseVO implements Serializable {
    /**
     * 通用状态，2000 代表操作成功；
     */
    public static final int RESP_SUCCESS = 2000;
    /**
     * 通用状态，2001 代表未查询到结果；
     */
    public static final int NULL_RESULT = 2001;
    /**
     * 通用状态，2020 代表数据更新失败；
     */
    public static final int UPDATE_FAIL = 2020;
    /**
     * 通用状态，2030 代表数据删除失败；
     */
    public static final int DELETE_FAIL = 2030;
    /**
     * 通用状态，2050 代表程序出现异常；
     */
    public static final int RESP_ERROR = 2050;


    /**
     * 用户通用状态，3010 代表用户输入的账号为空；
     */
    public static final int NULL_ACCOUNT = 3010;
    /**
     * 用户通用状态，3015 代表用户输入的账号不对（比如数字或字母）；
     */
    public static final int NUMBER_ACCOUNT = 3015;
    /**
     * 用户通用状态，3020 代表密码错误；
     */
    public static final int PASSWORD_ERROR = 3020;
    /**
     * 用户通用状态，3021 代表账号已经被注册；
     */
    public static final int EXIST_ACCOUNT = 3021;
    /**
     * 用户通用状态，3022 代表登录信息过期；
     */
    public static final int LOGIN_EXPIRED = 3022;
    /**
     * 用户通用状态，3040 代表邮箱输入的格式有误；
     */
    public static final int EMAIL_FORMAT = 3040;


    /**
     * 通用响应状态的标识区间
     */
    public static final int[] RESP_SHARED_TYPE = {2000, 2099};
    /**
     * 用户响应状态的标识区间
     */
    public static final int[] RESP_USER_TYPE = {3000, 3099};


    /**
     * 响应状态码，记录服务器的响应状态
     */
    private Integer code;
    private String msg;
    private Object data;

    public ResponseVO() {
    }

    public void setCode(int code) {
        this.code = code;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public void setData(Object data) {
        this.data = data;
    }

    /**
     * 快速构建一个响应实例，默认是构建一个响应 200 的实例对象
     * dataState 数据标识的状态。
     * 根据标记状态封装响应状态码和错误信息，如果传递的不是以下列出的任意值之一，
     * 均可构建一个响应 200 的空实例返回。部分响应码如下，更多响应码详情见常量类
     * -101 标识构建一个响应账户不存在的实例
     */
    public static ResponseVO buildSuccessInstance(Object data) {
        return buildSuccessInstance(RESP_SUCCESS, data);
    }

    public static ResponseVO buildSuccessInstance(int dataState, Object data) {
        InstanceBuild build = new InstanceBuild();
        ResponseVO vo = build.build(dataState);
        vo.setData(data);
        return vo;
    }

    public static ResponseVO buildSuccessInstance(int dataState) {
        InstanceBuild build = new InstanceBuild();
        return build.build(dataState);
    }

    public static ResponseVO buildSuccessInstance(Integer dataState) {
        if (dataState == null) {
            throw new RuntimeException("请不要使用空状态码创建响应类");
        }
        return buildSuccessInstance(dataState.intValue());
    }

    /**
     * 通过静态内部类 InstanceBuild 构建对象
     */
    public static class InstanceBuild {
        private int code;
        private String msg;
        private Object data;

        public InstanceBuild setCode(int code) {
            this.code = code;
            return this;
        }

        public InstanceBuild setMsg(String msg) {
            this.msg = msg;
            return this;
        }

        public InstanceBuild setData(Object data) {
            this.data = data;
            return this;
        }

        // 此处会使用到 spring-boot 的依赖
        public ResponseVO build() {
            // 如果没有设置响应状态码就将响应状态码设置为 200
            if (ObjectUtils.isEmpty(this.code) || this.code == 0) {
                this.code = 200;
            }
            return createResponseVO();
        }

        public ResponseVO build(int dataState) {
            if (internalElement(dataState, RESP_SHARED_TYPE)) {
                buildSharedResponseVO(dataState);
            }
            if (internalElement(dataState, RESP_USER_TYPE)) {
                buildUserResponseVO(dataState);
            }
            return build();
        }


        /**
         * 构建响应用户模块的模型
         */
        private void buildUserResponseVO(int dataState) {
            if (dataState == NULL_ACCOUNT) {
                this.code = 1000;
                this.msg = "账户不存在";
            }
            if (dataState == EXIST_ACCOUNT) {
                this.code = 1001;
                this.msg = "该账户已经被注册";
            }
            if (dataState == PASSWORD_ERROR) {
                this.code = 1002;
                this.msg = "密码错误";
            }
            if (dataState == NUMBER_ACCOUNT) {
                this.code = 1003;
                this.msg = "账户格式不对";
            }
            if (dataState == EMAIL_FORMAT) {
                this.code = 1005;
                this.msg = "邮箱格式不对";
            }
            if (dataState == LOGIN_EXPIRED) {
                this.code = 1004;
                this.msg = "登录信息已过期";
                this.data = "login.html";
            }
        }

        /**
         * 构建通用模块的响应模型
         */
        private void buildSharedResponseVO(int dataState) {
            // 设置成功响应
            if (dataState == RESP_SUCCESS) {
                this.code = 200;
                this.msg = "OK";
            }
            if (dataState == NULL_RESULT) {
                this.code = 404;
                this.msg = "查询结果为空！";
            }
            if (dataState == UPDATE_FAIL) {
                this.code = 505;
                this.msg = "更新失败！";
            }
            if (dataState == DELETE_FAIL) {
                this.code = 506;
                this.msg = "删除失败！";
            }
            if (dataState == RESP_ERROR) {
                this.code = 500;
                this.msg = "程序出现未知错误！请到后台调试";
            }
        }


        /**
         * 判断一个数字是否在指定区间内
         *
         * @param element  需要判断的状态
         * @param interval 响应状态的标识区间
         */
        private boolean internalElement(int element, int[] interval) {
            // 获取区间最小值
            int minInterval = interval[0];
            // 获取区间最大值
            int maxInterval = interval[1];
            return Math.max(minInterval, element) == Math.min(element, maxInterval);
        }

        private ResponseVO createResponseVO() {
            ResponseVO vo = new ResponseVO();
            vo.setCode(this.code);
            vo.setMsg(this.msg);
            vo.setData(this.data);
            return vo;
        }
    }
}
